package com.hxht.cmp.shiro;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import javax.annotation.Resource;
import javax.servlet.Filter;

import com.hxht.cmp.entity.Role;
import com.hxht.cmp.mapper.RoleMapper;
import com.hxht.cmp.mapper.UserRoleMapper;
import org.apache.shiro.config.Ini;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.Nameable;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.config.IniFilterChainResolverFactory;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;


public class ShiroPermissionFactory extends ShiroFilterFactoryBean {
    private final Logger logger = LoggerFactory.getLogger(ShiroPermissionFactory.class);
    /**
     * 配置中的过滤链
     */
    public static String definitions;

    /**
     * 权限service
     */
    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private RoleMapper roleMapper;

    private DefaultFilterChainManager filterManager;

    /**
     * 从数据库动态读取权限
     */
    @Override
    public void setFilterChainDefinitions(String definitions) {

        ShiroPermissionFactory.definitions = definitions;  //此时的参数就是配置文件里面的字符窜
        definitions = "";
        //数据库动态权限  
//        List<Permissions> permissions = permissionsMapper.selectAllPermissions();
        List<Map<String, String>> urlAndRoles = userRoleMapper.getShiroUrlAndRole();
        List<String> urls = new ArrayList<>();
        for (Map<String, String> urlAndRole : urlAndRoles) {
            if (!urls.contains(urlAndRole.get("url"))) {
                urls.add(urlAndRole.get("url"));
            }
        }

        //登录登出等写死
        StringBuilder definitionsBuilder = new StringBuilder(definitions
                + "/login = anon\n"
                + "/login.html = anon\n"
                + "/*.mp4 = anon\n"
                + "/basis/** = anon\n"
                + "/oauth/** = anon\n"
                + "/oauth/signOut = logout\n"
                + "/error/** = anon\n"
                + "/denied = anon\n"
                + "/assets/** = anon\n");
        for (String url : urls) {
            StringBuilder temp = new StringBuilder(url + " = anyRoles[");
            for (Map<String, String> urlAndRole : urlAndRoles) {
                if (Objects.equals(urlAndRole.get("url"), url)) {
                    temp.append(urlAndRole.get("authority")).append(",");
                }
            }
            temp = new StringBuilder(temp.substring(0, temp.length() - 1) + "]\n");
            definitionsBuilder.append(temp);
        }
        List<Role> roles = roleMapper.selectAll();
        if (!ObjectUtils.isEmpty(roles)) {
            definitionsBuilder .append("/**=anyRoles[");
            for (int i=0;i<roles.size();i++) {
                if (i==(roles.size()-1)) {
                    definitionsBuilder.append(roles.get(i).getAuthority());
                }else {
                    definitionsBuilder.append(roles.get(i).getAuthority()).append(",");
                }
            }
            definitionsBuilder.append("]\n");
        }else {
            definitionsBuilder.append("/**=anyRoles[ROLE_SUPER]\n");
        }
        definitions = definitionsBuilder.toString();
        logger.info(definitions);

        //从配置文件加载权限配置
        Ini ini = new Ini();
        ini.load(definitions);
        Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
        if (CollectionUtils.isEmpty(section)) {
            section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
        }

        //加入权限集合  
        setFilterChainDefinitionMap(section);
    }


    public void reloadShiro() {
        try {
            //1、首先删除以前老的filter chain并注册默认的  
            filterManager.getFilterChains().clear();
//            filterManager.getFilters().clear();
            setFilterChainDefinitions("aa");
            createFilterChainManager();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    @Override
    public FilterChainManager createFilterChainManager() {

        Map<String, Filter> defaultFilters = filterManager.getFilters();

        //apply global settings if necessary:
        for (Filter filter : defaultFilters.values()) {
            applyGlobalPropertiesIfNecessary(filter);
        }

        //Apply the acquired and/or configured filters:
        Map<String, Filter> filters = getFilters();
        if (!CollectionUtils.isEmpty(filters)) {
            for (Entry<String, Filter> entry : filters.entrySet()) {
                String name = entry.getKey();
                Filter filter = entry.getValue();
                applyGlobalPropertiesIfNecessary(filter);
                if (filter instanceof Nameable) {
                    ((Nameable) filter).setName(name);
                }
                //'init' argument is false, since Spring-configured filters should be initialized
                //in Spring (i.e. 'init-method=blah') or implement InitializingBean:
                filterManager.addFilter(name, filter, false);
            }
        }

        //build up the chains:
        Map<String, String> chains = getFilterChainDefinitionMap();
        if (!CollectionUtils.isEmpty(chains)) {
            for (Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue();
                filterManager.createChain(url, chainDefinition);
            }
        }
        return filterManager;
    }

    //以下四个方法为父类的私有方法
    private void applyGlobalPropertiesIfNecessary(Filter filter) {
        applyLoginUrlIfNecessary(filter);
        applySuccessUrlIfNecessary(filter);
        applyUnauthorizedUrlIfNecessary(filter);
    }


    private void applyLoginUrlIfNecessary(Filter filter) {
        String loginUrl = getLoginUrl();
        if (StringUtils.hasText(loginUrl) && (filter instanceof AccessControlFilter)) {
            AccessControlFilter acFilter = (AccessControlFilter) filter;
            //only apply the login url if they haven't explicitly configured one already:
            String existingLoginUrl = acFilter.getLoginUrl();
            if (AccessControlFilter.DEFAULT_LOGIN_URL.equals(existingLoginUrl)) {
                acFilter.setLoginUrl(loginUrl);
            }
        }
    }

    private void applySuccessUrlIfNecessary(Filter filter) {
        String successUrl = getSuccessUrl();
        if (StringUtils.hasText(successUrl) && (filter instanceof AuthenticationFilter)) {
            AuthenticationFilter authcFilter = (AuthenticationFilter) filter;
            //only apply the successUrl if they haven't explicitly configured one already:
            String existingSuccessUrl = authcFilter.getSuccessUrl();
            if (AuthenticationFilter.DEFAULT_SUCCESS_URL.equals(existingSuccessUrl)) {
                authcFilter.setSuccessUrl(successUrl);
            }
        }
    }

    private void applyUnauthorizedUrlIfNecessary(Filter filter) {
        String unauthorizedUrl = getUnauthorizedUrl();
        if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) {
            AuthorizationFilter authzFilter = (AuthorizationFilter) filter;
            //only apply the unauthorizedUrl if they haven't explicitly configured one already:
            String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();
            if (existingUnauthorizedUrl == null) {
                authzFilter.setUnauthorizedUrl(unauthorizedUrl);
            }
        }
    }


    public DefaultFilterChainManager getFilterManager() {
        return filterManager;
    }

    public void setFilterManager(DefaultFilterChainManager filterManager) {
        this.filterManager = filterManager;
    }
}
